#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "user_define.h"
#include "action_define.h"
#include "TAC_conn_struct.h"

// add para lib_include

char Buf[DIGEST_SIZE*4];

int PM_server_init(void * sub_proc, void * para)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * local_state;
	// add yorself's module init func here
	local_state=Dalloc0(sizeof(*local_state),NULL);
	if(local_state==NULL)
		return -ENOMEM;

    local_state->type=TAC_NODE_PM;
    local_state->state=TAC_STATE_INIT;
	proc_share_data_getvalue("uuid",local_state->node_uuid);	
	proc_share_data_getvalue("proc_name",local_state->proc_name);	
    
	ex_module_setpointer(sub_proc,local_state);	
	return 0;
}

int PM_server_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	RECORD(TAC_DEFINE,CONN_STATE) * client_state;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		client_state = ex_module_getpointer(sub_proc);	
		if(client_state->state == TAC_STATE_INIT)
		{
			if((type==TYPE(TAC_DEFINE))&&(subtype==SUBTYPE(TAC_DEFINE,LOGIN_USER)))
			{
				ret=proc_PM_remotelogin(sub_proc,recv_msg);
			}
			if((type==TYPE(TAC_DEFINE))&&(subtype==SUBTYPE(TAC_DEFINE,ACTION)))
			{
				ret=proc_PM_action(sub_proc,recv_msg);
			}
		}
	}
	return 0;
}

int proc_PM_remotelogin(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * client_state;
	RECORD(TAC_DEFINE,LOGIN_USER) * login_info;
	RECORD(TAC_DEFINE,LOGIN_CHALLENGE) * login_challenge;
	RECORD(TAC_DEFINE,LOGIN_RETURN) * return_info;
	void * new_msg;
	
	ret=message_get_record(recv_msg,&login_info,0);
	if(ret<0)
		return ret;


	DB_RECORD * db_record;

	db_record=memdb_find_first(TYPE_PAIR(TAC_DEFINE,CONN_STATE),"user",login_info->user);
	if(db_record==NULL)
	{
	    return_info=Talloc0(sizeof(*return_info));
	    if(return_info==NULL)
		    return -ENOMEM;
		return_info->return_code=NOUSER;
		return_info->return_info=dup_str("no such user!\n",0);
	    new_msg=message_create(TYPE_PAIR(TAC_DEFINE,LOGIN_RETURN),recv_msg);	
	    if(new_msg==NULL)
		    return -EINVAL;
	    ret=message_add_record(new_msg,return_info);
	    if(ret<0)
		    return ret;
	
	}
	else
	{
		client_state=db_record->record;
		if(client_state->state==0)
		{
			// enter the challenge mode
		    client_state->state=TAC_STATE_CHALLENGE;
			Memcpy(client_state->proc_name,login_info->proc_name,DIGEST_SIZE);
			Memcpy(client_state->node_uuid,login_info->uuid,DIGEST_SIZE);
			RAND_bytes(client_state->nonce,DIGEST_SIZE);

	        login_challenge=Talloc0(sizeof(*login_challenge));
	        if(login_challenge==NULL)
		        return -ENOMEM;

            Memcpy(login_challenge->uuid,login_info->uuid,DIGEST_SIZE);
            Strncpy(login_challenge->user,login_info->user,DIGEST_SIZE);
			login_challenge->retval=TAC_ACTION_CHALLENGE;
			Memcpy(login_challenge->nonce,client_state->nonce,DIGEST_SIZE);
	        new_msg=message_create(TYPE_PAIR(TAC_DEFINE,LOGIN_CHALLENGE),recv_msg);	
	        if(new_msg==NULL)
		        return -EINVAL;
	        ret=message_add_record(new_msg,login_challenge);
	        if(ret<0)
		        return ret;
		    memdb_store(client_state,TYPE_PAIR(TAC_DEFINE,CONN_STATE),NULL);
		}
		else if (client_state->state ==TAC_STATE_CHALLENGE)
		{
	        return_info=Talloc0(sizeof(*return_info));
	        if(return_info==NULL)
		        return -ENOMEM;
			// enter the verify mode
			Memset(Buf,0,DIGEST_SIZE);
			Memcpy(Buf,client_state->nonce,DIGEST_SIZE);
            calculate_context_sm3(client_state->passwd,DIGEST_SIZE,Buf+DIGEST_SIZE);
			calculate_context_sm3(Buf,DIGEST_SIZE*2,Buf+DIGEST_SIZE*2);
		
			if(Memcmp(Buf+DIGEST_SIZE*2,login_info->passwd,DIGEST_SIZE)==0)
			{
				return_info->return_code=SUCCEED;
				return_info->return_info=dup_str("login succeed!\n",0);
			}
			else
			{
				return_info->return_code=AUTHFAIL;
				return_info->return_info=dup_str("password error!\n",0);
			}
	        new_msg=message_create(TYPE_PAIR(TAC_DEFINE,LOGIN_RETURN),recv_msg);	
	        if(new_msg==NULL)
		        return -EINVAL;
	        ret=message_add_record(new_msg,return_info);
	        if(ret<0)
		        return ret;
		    client_state->state=return_info->return_code;
		    memdb_store(client_state,TYPE_PAIR(TAC_DEFINE,CONN_STATE),NULL);
		}
	}
				
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}
int proc_PM_action(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(TAC_DEFINE,CONN_STATE) * client_state;
	RECORD(TAC_DEFINE,ACTION) * TAC_action;
	RECORD(TAC_DEFINE,LOGIN_RETURN) * return_info;
	void * new_msg;
    char err_msg[32];
	
	ret=message_get_record(recv_msg,&TAC_action,0);
	if(ret<0)
		return ret;

	DB_RECORD * db_record;

	db_record=memdb_find_first(TYPE_PAIR(TAC_DEFINE,CONN_STATE),"user",TAC_action->user);
	if(db_record==NULL)
	{
        ret=TAC_ACTION_ERR_NOUSER;
        Strncpy(err_msg,"no such user!",DIGEST_SIZE);
        goto action_error;
    }	

	client_state=db_record->record;
    if(Memcmp(client_state->node_uuid,TAC_action->uuid,DIGEST_SIZE)!=0)
    {
        ret=TAC_ACTION_ERR_NOUSER;
        Strncpy(err_msg,"user node changes!",DIGEST_SIZE);
        goto action_error;
    }	

    switch(TAC_action->action)
    {
        case TAC_ACTION_SESSIONKEYSEND:
            TAC_action->action=TAC_ACTION_SESSIONKEYGET;
		    client_state->state=TAC_STATE_SYMMKEYREADY;
            break;
        case TAC_ACTION_PLATFORMREPORT:
            TAC_action->action=TAC_ACTION_PLATFORMVERIFY;
		    client_state->state=TAC_STATE_PLATFORMVERIFY;
            break;

        default:
            ret=TAC_ACTION_ERR_KEYFAIL;
            Strncpy(err_msg,"invalid action!",DIGEST_SIZE);
            goto action_error;
    }

	new_msg=message_create(TYPE_PAIR(TAC_DEFINE,ACTION),recv_msg);	
	if(new_msg==NULL)
		 return -EINVAL;
	ret=message_add_record(new_msg,TAC_action);
	if(ret<0)
		 return ret;
	memdb_store(client_state,TYPE_PAIR(TAC_DEFINE,CONN_STATE),NULL);
	ret=ex_module_sendmsg(sub_proc,new_msg);
    return ret;

action_error:

	return_info=Talloc0(sizeof(*return_info));
	if(return_info==NULL)
	    return -ENOMEM;
	return_info->return_code=ret;
	return_info->return_info=dup_str(err_msg,DIGEST_SIZE);
	new_msg=message_create(TYPE_PAIR(TAC_DEFINE,LOGIN_RETURN),recv_msg);	
	if(new_msg==NULL)
	    return -EINVAL;
	ret=message_add_record(new_msg,return_info);
	if(ret<0)
        return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);
    return ret;
}

